using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Akcedo.Framework.Infrastructure.ASPNet;
using FwkLight.CodeExtensions;
using FwkLight.Domain.Fetching;
using FwkLight.Domain.Repository;
using FwkLight.Persistence;
using StructureMap;

namespace FwkLight.Application
{
  public abstract class BaseLoadTask<EntityType> : BaseApplicationTask<EntityType>
  {
    private readonly IBaseRepo<EntityType> _repository;
    private IFetchingStrategy<EntityType> _fetchingStrategy;
    private readonly IList<IPartialFetchingStrategy<EntityType>> _partialFetchingStrategies;

    [DebuggerNonUserCodeAttribute]
    protected BaseLoadTask ( IBaseRepo<EntityType> repository, INHUnitOfWorkProvider uowProvider )
      : base(uowProvider)
    {
      _repository = repository;
      _partialFetchingStrategies = new List<IPartialFetchingStrategy<EntityType>>();
    }

    protected IBaseRepo<EntityType> Repository
    {
      [DebuggerNonUserCodeAttribute]
      get { return _repository; }
    }

    protected IMultiCriteriaFetcher<EntityType> Fetcher
    {
      [DebuggerNonUserCodeAttribute]
      get
      {
        return _repository.MultiCriteriaFetcher;
      }
    }

    protected override void ExecuteInternal()
    {
      LoadDataNeccesaryForLoadEntityDecisions();
      ConfigureRepositoryBeforeLoad();
      LoadEntity();
      base.ExecuteInternal();

      _partialFetchingStrategies.Clear();
    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void LoadDataNeccesaryForLoadEntityDecisions()
    {
    }

    [DebuggerNonUserCodeAttribute]
    private void ConfigureRepositoryBeforeLoad ( )
    {
      //_repository.InitialiseCriteria();

      ApplyFetching();
      ConfigureFilters();
    }

    protected virtual void ApplyFetching()
    {
      _repository.MultiCriteriaFetcher.Clear();

      IList<FetchingConfiguration> fetchingConfig =
        RequestItemDictionary.Get(ApplicationConstants.Request.FetchingConfigurationList);
      if (fetchingConfig != null && fetchingConfig.Any(p => p.TaskType == GetType()))
      {
        FetchingConfiguration configuration = fetchingConfig.FirstOrDefault(p => p.TaskType == GetType());

        if (configuration != null)
        {
          Type fetchingType = configuration.FetchingType;
          var strategy = ObjectFactory.GetInstance(fetchingType) as IFetchingStrategy<EntityType>;

          if (strategy != null)
            strategy.ApplyStrategyOn(_repository.MultiCriteriaFetcher);
        }
      }
      else if (_fetchingStrategy != null)
        _fetchingStrategy.ApplyStrategyOn(_repository.MultiCriteriaFetcher);

      if (_partialFetchingStrategies.Count > 0)
        _partialFetchingStrategies.ForEach(p => p.ApplyStrategyOn(_repository.MultiCriteriaFetcher));
    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void ConfigureFilters ( ) { }

    [DebuggerNonUserCodeAttribute]
    protected virtual void LoadEntity ( )
    {
      Entity = _repository.LoadEntity();
    }

    [DebuggerNonUserCodeAttribute]
    public void ForFetchingUse ( IFetchingStrategy<EntityType> fetchingStrategy )
    {
      _fetchingStrategy = fetchingStrategy;
    }

    public void AddPartialFetching(IPartialFetchingStrategy<EntityType> partialFetchingStrategy, out bool hasChanges)
    {
      hasChanges = PartialFetchingIsNotCoveredByCurrentFetching(partialFetchingStrategy);
      _partialFetchingStrategies.Add(partialFetchingStrategy);
    }

    private bool PartialFetchingIsNotCoveredByCurrentFetching(IPartialFetchingStrategy<EntityType> partialFetchingStrategy)
    {
      ApplyFetching();
      partialFetchingStrategy.ApplyStrategyOn(_repository.MultiCriteriaFetcher);
      return partialFetchingStrategy.HasChanges;
    }
  }
}